/**
 * Copyright (c) 2006-2020, zwl
 * 
 * description  低功耗封装组件
 * 
 * Change Logs:
 * Date             Author         Notes
 * 2021-10-29       zwl             [0.0.1]创建
 * 
 */
#include "EntTypeFramework.h"
#include "Bsp.h"
#include "low_power.h"

#if USE_LP

//当没有任何事件时，默认休眠时间
static etf_uint32_t                 IdleLpTimer						= LP_IDLE_LP_TIMER;

/* ***************************************** API Interface Start ***************************************** */
/* ------------------------------------------------------------------------------------------------------- */
volatile static etf_uint32_t		s_elapsed_count                	= 0;            //经过的时间s
volatile static etf_uint32_t		s_set_count                		= 0;            //设置的时间s

volatile static etf_uint32_t		s_remaining_count              	= 0;            //剩余的时间s
volatile static etf_uint32_t		ms_remaining_sub_count			= 0;			//剩余时间ms

volatile static etf_uint16_t		lp_tim_compensate_value			= 0;			//补偿时需要计算的值
volatile static etf_uint16_t		lp_tim_surplus_arp_value		= 0;			//定时器最大计时2s，当定时值不满足2s或不是2s整数倍时，剩余的定时器计数值

volatile static etf_uint16_t		lp_tim_reset_apr_flag			= 0;			//重新设置APR时的标志（逻辑是中断产生后已经完成了APR重新加载，应该在倒数
																					//第二次中断的时候重新设置APR，置位标志，倒数第一次中断时不关中断，会加载
																					//上一次设置的APR，在一次中断关闭定时器，完成定时）
/**
 * 设置休眠时的低功耗定时器定时值
 * 由于硬件定时器限制，这里设置为1ms唤醒一次，通过一个变量累加的方式继续休眠计数
 *@param :  定时时间
 *
 *@return:
 *
 *@note  : 
 * 
 *
 */
void LP_SetWakeUpTimer(etf_uint32_t timeoutValue){
    
	etf_uint32_t					ms_timeoutValue;
	etf_uint16_t					u16TimeOut				= 1000;
	
    Sysctrl_SetRCLTrim(SysctrlRclFreq32768);
    //使能RCL
    Sysctrl_ClkSourceEnable(SysctrlClkRCL, TRUE);
    
    stc_lptim_cfg_t                 stcLptCfg;
    DDL_ZERO_STRUCT(stcLptCfg);
	
	s_elapsed_count                                                	= 0;												//定时器经过的时间，以2s为单位
	ms_timeoutValue													= etf_from_tick_to_ms(timeoutValue);//timeoutValue*(1000/ETF_TIMER_CLOCK_SEC);			//需要定时的时间，单位ms
	
    s_remaining_count                                              	= ms_timeoutValue/2000;								//0~0xFFFF,在32.768频率下为2s，定时的时间包含几个2s
	ms_remaining_sub_count											= ms_timeoutValue%2000;								//不足2s或不是2s倍数时余下的ms值
	s_set_count														= s_remaining_count;
	
	lp_tim_surplus_arp_value										= ms_remaining_sub_count/(1.0/32.768);				//余下的ms时间转换为定时器值
	lp_tim_compensate_value											= lp_tim_surplus_arp_value;
	
	lp_tim_reset_apr_flag											= 0;
    ///< 使能LPTIM0 外设时钟
    Sysctrl_SetPeripheralGate(SysctrlPeripheralLpTim, TRUE);

    stcLptCfg.enGate   = LptimGateLow;
    stcLptCfg.enGatep  = LptimGatePLow;
    stcLptCfg.enTcksel = LptimRcl;
    stcLptCfg.enTogen  = LptimTogEnLow;
    stcLptCfg.enCt     = LptimTimerFun;                                         //计数器功能
    stcLptCfg.enMd     = LptimMode2;                                            //工作模式为模式2：自动重装载16位计数器/定时器
	
	if(0 == s_remaining_count){
		stcLptCfg.u16Arr   = 0xFFFF - lp_tim_surplus_arp_value;					//预装载寄存器值
		lp_tim_surplus_arp_value									= 0;
	} else {
		stcLptCfg.u16Arr   = 0;
	}
    Lptim_Init(M0P_LPTIMER, &stcLptCfg);

    Lptim_ClrItStatus(M0P_LPTIMER);                                             //清除中断标志位
    Lptim_ConfIt(M0P_LPTIMER, TRUE);                                            //允许LPTIMER中断
    EnableNvic(LPTIM_IRQn, IrqLevel3, TRUE);
    
    Lptim_Cmd(M0P_LPTIMER, TRUE);                                               //启动lptimer0运行
	
	if( (1 == s_remaining_count) && (0 != lp_tim_surplus_arp_value) ){
			    
		while(u16TimeOut--){
			if(M0P_LPTIMER->CR_f.WT_FLAG){
				break;
			}
		}
		M0P_LPTIMER->ARR_f.ARR							= 0xFFFF - lp_tim_surplus_arp_value;
		lp_tim_surplus_arp_value						= 0;
		lp_tim_reset_apr_flag							= 1;
	} 
}

/**
 * 清除休眠时的低功耗定时器
 *
 *@param :
 *
 *@return:
 *
 *@note  : 
 * 
 *
 */
void LP_ClrWakeUpTimer(void){
	
    Lptim_Cmd(M0P_LPTIMER, FALSE);                                  //停止lptimer0运行
    Lptim_ConfIt(M0P_LPTIMER, FALSE);                               //禁止LPTIMER中断
    EnableNvic(LPTIM_IRQn, IrqLevel3, FALSE);
    Sysctrl_ClkSourceEnable(SysctrlClkRCL, FALSE);                  //停止RCL
}

/**
 * 获取休眠时的低功耗定时器的计数时间值，单位tick
 *
 *@param :
 *
 *@return:  休眠时定时时间，需转换为系统tick后返回
 *
 *@note  : 
 * 
 *
 */
clock_time_t LP_GetWakeUpTimerCount(void){
	etf_uint32_t					s_elapsed_sub_count;
	etf_uint32_t					ms_sub_time, ms_elapsed_time;
	
	etf_uint16_t					temp;
	
	//2s中断一次,后余下的时间
	s_elapsed_sub_count										= M0P_LPTIMER->CNT_f.CNT;
	
//	printf("1*** %d-%d-%d-%d-%d \r\n", ms_elapsed_time, lp_tim_compensate_value, s_elapsed_sub_count, s_set_count ,s_elapsed_count);
	
	if(s_set_count == s_elapsed_count){
		
		if(0 != lp_tim_compensate_value){
			temp											= 0xFFFF - lp_tim_compensate_value;
			if(temp < s_elapsed_sub_count){
				lp_tim_compensate_value						= lp_tim_compensate_value + (s_elapsed_sub_count - temp);
			} else {
				lp_tim_compensate_value						= temp;
			}
		}
	} else {
		lp_tim_compensate_value								= s_elapsed_sub_count;
	}
	
	//转为ms
	ms_sub_time												= (etf_uint32_t)((double)lp_tim_compensate_value*(1.0/32.768));
	
	//一共经过的时间
	ms_elapsed_time											= (s_elapsed_count*2000)+ms_sub_time;
//	printf("2*** %d-%d-%d \r\n", ms_elapsed_time, lp_tim_compensate_value, s_elapsed_sub_count);
	printf("LP_CompensateClock %d ms\r\n", ms_elapsed_time);
	//转换为tick返回
    return etf_from_ms_to_tick(ms_elapsed_time);
//	return 0;
}

/**
 * 低功耗定时器中断函数
 *
 *@param :
 *
 *@return:
 *
 *@note  : 
 * 
 *
 */
void LpTim_IRQHandler(void)
{

	etf_uint16_t					u16TimeOut				= 1000;

    if (TRUE == Lptim_GetItStatus(M0P_LPTIMER)){
        //清除LPTIMER0的中断标志位
        Lptim_ClrItStatus(M0P_LPTIMER);
        
		if(s_remaining_count){
			s_remaining_count--;
			//休眠时间计数增加
			s_elapsed_count++;
		}
		
		if( (1 == s_remaining_count) && (0 != lp_tim_surplus_arp_value) ){
			    
			while(u16TimeOut--){
				if(M0P_LPTIMER->CR_f.WT_FLAG){
					break;
				}
			}
			M0P_LPTIMER->ARR_f.ARR							= 0xFFFF - lp_tim_surplus_arp_value;
			lp_tim_surplus_arp_value						= 0;
			lp_tim_reset_apr_flag							= 1;
		} else if( (0 == s_remaining_count) && (0 == lp_tim_surplus_arp_value) ){
			//时间到，退出中断后不在进入休眠
			if(1 == lp_tim_reset_apr_flag){
				lp_tim_reset_apr_flag						= 2;
				return;
			}
            LP_SleepExit();
		}

    }
}
/* ------------------------------------------------------------------------------------------------------- */
/* ****************************************** API Interface End ****************************************** */

volatile static LP_SLEEP_LOCK_STA_E	LP_SleepLockFlag		= LP_SLEEP_LOCK_STA;						//休眠锁，上电默认加锁
volatile static LP_SLEEP_MODE_E		LP_SleepMode			= LP_SLEEP_MODE_FALSE;						//默认不工作与低功耗模式

/**
 * 进入休眠模式
 *
 *@param :
 *
 *@return:
 *
 *@note  : 
 * 
 *
 */
void LP_EnterSleep(void){
	LP_SleepMode										= LP_SLEEP_MODE_TRUE;
	
    //进入深度睡眠模式，中断唤醒执行完中断程序后会自动再次进入休眠
	Lpm_GotoDeepSleep(TRUE);
//    //关闭低功耗定时器
//    LP_ClrWakeUpTimer();
}

/**
 * 退出休眠，中断唤醒时可调用
 *
 *@param :
 *
 *@return:
 *
 *@note  : 
 * 
 *
 */
void LP_SleepExit(void){
	
	if(LP_SLEEP_MODE_TRUE == LP_SleepMode){
		LP_EXIT_SLEEP();
		LP_ClrWakeUpTimer();
		LP_SleepMode										= LP_SLEEP_MODE_FALSE;
	}
}

//休眠锁嵌套计数
static etf_uint32_t				sleep_lock_nested_count		= 0;
/**
 * 休眠加锁，加锁后不执行休眠判断
 *
 *@param :
 *
 *@return:
 *
 *@note  : 
 * 
 *
 */
void LP_SleepLock(void){
	sleep_lock_nested_count++;
	LP_SleepLockFlag										= LP_SLEEP_LOCK_STA;
}

/**
 * 休眠解锁，解锁后执行休眠判断，满足空闲条件自动进入睡眠状态
 *
 *@param :
 *
 *@return:
 *
 *@note  : 
 * 
 *
 */
void LP_SleepUnLock(void){
	if(0 != sleep_lock_nested_count){
		sleep_lock_nested_count--;
	}
	
	if(0 == sleep_lock_nested_count){
		LP_SleepLockFlag									= LP_SLEEP_UNLOCK_STA;
	}
}

/**
 * 获取休眠锁状态
 *
 *@param :
 *
 *@return: 休眠锁状态
 *
 *@note  : 
 * 
 *
 */
LP_SLEEP_LOCK_STA_E LP_SleepLockStaGet(void){
	return LP_SleepLockFlag;
}

/**
 * 获取当前是否是在休眠模式
 *
 *@param :
 *
 *@return:  是否在休眠模式
 *
 *@note  : 
 * 
 *
 */
LP_SLEEP_MODE_E LP_SleepModeGet(void){
	return LP_SleepMode;
}
/**
 * 心跳时钟补偿函数，休眠时低功耗定时器进行时间计数，唤醒后需要补偿
 *
 *@param :
 *
 *@return:
 *
 *@note  : 
 * 
 *
 */
void LP_CompensateClock(void){
	etf_uint32_t                    ElapsedTime;
	
	ETF_IRQ_DISABLE( );
	
	ElapsedTime                                                     = LP_GetWakeUpTimerCount();
	
	etf_timer_set_clock(etf_timer_get_clock() + ElapsedTime);
	
	ETF_IRQ_ENABLE( );
}
clock_time_t etf_timer_get_next_expiration2(void);
/**
 * 进入低功耗处理程序
 *
 *@param :
 *
 *@return:
 *
 *@note  : 
 * 
 *
 */
void LP_process_run(void){
	volatile clock_time_t                    LpTimerValue;
	
	if(LP_SLEEP_LOCK_STA == LP_SleepLockStaGet()){
		return;
	}
	
    //计算休眠时间
    ETF_IRQ_DISABLE( );
    LpTimerValue													= etf_timer_get_next_expiration();
	ETF_IRQ_ENABLE( );
	
    if( IdleLpTimer <= LpTimerValue){
        LpTimerValue												= IdleLpTimer - etf_from_ms_to_tick(25);
    } else if(LP_IDLE_LP_TIMER_LIMIT < LpTimerValue){
        LpTimerValue												= LpTimerValue - etf_from_ms_to_tick(25);
    } else {
        LpTimerValue												= 0;
    }
    
    
    
    if(0 != LpTimerValue){
		printf("Enter Sleep [%d ms]\r\n", etf_from_tick_to_ms(LpTimerValue));
		
        //可以进入休眠，设置低功耗定时器，休眠时进行时钟计数
        LP_SetWakeUpTimer(LpTimerValue);
        //进入休眠
        LP_EnterSleep();
		//退出休眠模式
		LP_SleepExit();
        //时钟补偿
        LP_CompensateClock();
		printf("Exit Sleep\r\n");
    }

}


#endif



